#ifndef UnstructMG_PAR_VECTOR_HPP
#define UnstructMG_PAR_VECTOR_HPP

#include "seq_mv.hpp"
#include "_hypre_parcsr_mv.h"

template<typename idx_t, typename data_t, int dof>
class par_Vector
{
public:
    const MPI_Comm comm = MPI_COMM_NULL;// 通信域
    idx_t glb_nrows;// 向量的全局行数
    idx_t beg_row;// 本进程负责的起始行号
    idx_t end_row;// 本进程负责的末尾行号（注意是开区间）

    seq_Vector<idx_t, data_t, dof> * local_vector = nullptr;

    par_Vector(MPI_Comm _comm);
    par_Vector(MPI_Comm _comm, const idx_t _glb_nrows, const idx_t _my_beg, const idx_t _my_end);
    par_Vector(const par_Vector & model);
    ~par_Vector();

    void set_val(data_t val);
    void set_values_hypre(const HYPRE_ParVector hypre_vec);
};

#ifdef USE_FP16
typedef par_Vector<int, __fp16, 1> par_Vector_I32F16_DOF1;
typedef par_Vector<int, __fp16, 2> par_Vector_I32F16_DOF2;
typedef par_Vector<int, __fp16, 3> par_Vector_I32F16_DOF3;
typedef par_Vector<int, __fp16, 4> par_Vector_I32F16_DOF4;
#endif
typedef par_Vector<int, float , 1> par_Vector_I32F32_DOF1;
typedef par_Vector<int, float , 2> par_Vector_I32F32_DOF2;
typedef par_Vector<int, float , 3> par_Vector_I32F32_DOF3;
typedef par_Vector<int, float , 4> par_Vector_I32F32_DOF4;

typedef par_Vector<int, double, 1> par_Vector_I32F64_DOF1;
typedef par_Vector<int, double, 2> par_Vector_I32F64_DOF2;
typedef par_Vector<int, double, 3> par_Vector_I32F64_DOF3;
typedef par_Vector<int, double, 4> par_Vector_I32F64_DOF4;

template<typename idx_t, typename data_t, typename res_t, int dof>
res_t vec_dot(const par_Vector<idx_t, data_t, dof>  & x, const par_Vector<idx_t, data_t, dof> & y);
extern template double vec_dot(const par_Vector_I32F32_DOF1 &, const par_Vector_I32F32_DOF1 &);
extern template double vec_dot(const par_Vector_I32F32_DOF2 &, const par_Vector_I32F32_DOF2 &);
extern template double vec_dot(const par_Vector_I32F32_DOF3 &, const par_Vector_I32F32_DOF3 &);
extern template double vec_dot(const par_Vector_I32F32_DOF4 &, const par_Vector_I32F32_DOF4 &);
extern template double vec_dot(const par_Vector_I32F64_DOF1 &, const par_Vector_I32F64_DOF1 &);
extern template double vec_dot(const par_Vector_I32F64_DOF2 &, const par_Vector_I32F64_DOF2 &);
extern template double vec_dot(const par_Vector_I32F64_DOF3 &, const par_Vector_I32F64_DOF3 &);
extern template double vec_dot(const par_Vector_I32F64_DOF4 &, const par_Vector_I32F64_DOF4 &);

template<typename idx_t, typename data_t, typename res_t, int dof>
res_t vec_inf_norm(par_Vector<idx_t, data_t, dof> const & x);
extern template double vec_inf_norm(const par_Vector_I32F32_DOF1 &);
extern template double vec_inf_norm(const par_Vector_I32F32_DOF2 &);
extern template double vec_inf_norm(const par_Vector_I32F32_DOF3 &);
extern template double vec_inf_norm(const par_Vector_I32F32_DOF4 &);
extern template double vec_inf_norm(const par_Vector_I32F64_DOF1 &);
extern template double vec_inf_norm(const par_Vector_I32F64_DOF2 &);
extern template double vec_inf_norm(const par_Vector_I32F64_DOF3 &);
extern template double vec_inf_norm(const par_Vector_I32F64_DOF4 &);

// v1 + alpha * v2 -> v
template<typename idx_t, typename data_t, typename scalar_t, int dof>
void vec_add(const par_Vector<idx_t, data_t, dof> & v1, scalar_t alpha, 
             const par_Vector<idx_t, data_t, dof> & v2, par_Vector<idx_t, data_t, dof> & v);
extern template void vec_add(const par_Vector_I32F32_DOF1 &, float , const par_Vector_I32F32_DOF1 &, par_Vector_I32F32_DOF1 &);
extern template void vec_add(const par_Vector_I32F32_DOF2 &, float , const par_Vector_I32F32_DOF2 &, par_Vector_I32F32_DOF2 &);
extern template void vec_add(const par_Vector_I32F32_DOF3 &, float , const par_Vector_I32F32_DOF3 &, par_Vector_I32F32_DOF3 &);
extern template void vec_add(const par_Vector_I32F32_DOF4 &, float , const par_Vector_I32F32_DOF4 &, par_Vector_I32F32_DOF4 &);
extern template void vec_add(const par_Vector_I32F64_DOF1 &, double, const par_Vector_I32F64_DOF1 &, par_Vector_I32F64_DOF1 &);
extern template void vec_add(const par_Vector_I32F64_DOF2 &, double, const par_Vector_I32F64_DOF2 &, par_Vector_I32F64_DOF2 &);
extern template void vec_add(const par_Vector_I32F64_DOF3 &, double, const par_Vector_I32F64_DOF3 &, par_Vector_I32F64_DOF3 &);
extern template void vec_add(const par_Vector_I32F64_DOF4 &, double, const par_Vector_I32F64_DOF4 &, par_Vector_I32F64_DOF4 &);

// src -> dst
template<typename idx_t, typename data_t, int dof>
void vec_copy(const par_Vector<idx_t, data_t, dof> & src, par_Vector<idx_t, data_t, dof> & dst);
extern template void vec_copy(const par_Vector_I32F32_DOF1 &, par_Vector_I32F32_DOF1 &);
extern template void vec_copy(const par_Vector_I32F32_DOF2 &, par_Vector_I32F32_DOF2 &);
extern template void vec_copy(const par_Vector_I32F32_DOF3 &, par_Vector_I32F32_DOF3 &);
extern template void vec_copy(const par_Vector_I32F32_DOF4 &, par_Vector_I32F32_DOF4 &);
extern template void vec_copy(const par_Vector_I32F64_DOF1 &, par_Vector_I32F64_DOF1 &);
extern template void vec_copy(const par_Vector_I32F64_DOF2 &, par_Vector_I32F64_DOF2 &);
extern template void vec_copy(const par_Vector_I32F64_DOF3 &, par_Vector_I32F64_DOF3 &);
extern template void vec_copy(const par_Vector_I32F64_DOF4 &, par_Vector_I32F64_DOF4 &);

// coeff * src -> dst
template<typename idx_t, typename data_t, typename scalar_t, int dof>
void vec_mul_by_scalar(const scalar_t coeff, const par_Vector<idx_t, data_t, dof> & src, par_Vector<idx_t, data_t, dof> & dst);
extern template void vec_mul_by_scalar(const float , const par_Vector_I32F32_DOF1 &, par_Vector_I32F32_DOF1 &);
extern template void vec_mul_by_scalar(const float , const par_Vector_I32F32_DOF2 &, par_Vector_I32F32_DOF2 &);
extern template void vec_mul_by_scalar(const float , const par_Vector_I32F32_DOF3 &, par_Vector_I32F32_DOF3 &);
extern template void vec_mul_by_scalar(const float , const par_Vector_I32F32_DOF4 &, par_Vector_I32F32_DOF4 &);
extern template void vec_mul_by_scalar(const double, const par_Vector_I32F64_DOF1 &, par_Vector_I32F64_DOF1 &);
extern template void vec_mul_by_scalar(const double, const par_Vector_I32F64_DOF2 &, par_Vector_I32F64_DOF2 &);
extern template void vec_mul_by_scalar(const double, const par_Vector_I32F64_DOF3 &, par_Vector_I32F64_DOF3 &);
extern template void vec_mul_by_scalar(const double, const par_Vector_I32F64_DOF4 &, par_Vector_I32F64_DOF4 &);

// vec *= coeff
template<typename idx_t, typename data_t, typename scalar_t, int dof>
void vec_scale(const scalar_t coeff, par_Vector<idx_t, data_t, dof> & vec);
extern template void vec_scale(const float , par_Vector_I32F32_DOF1 & );
extern template void vec_scale(const float , par_Vector_I32F32_DOF2 & );
extern template void vec_scale(const float , par_Vector_I32F32_DOF3 & );
extern template void vec_scale(const float , par_Vector_I32F32_DOF4 & );
extern template void vec_scale(const double, par_Vector_I32F64_DOF1 & );
extern template void vec_scale(const double, par_Vector_I32F64_DOF2 & );
extern template void vec_scale(const double, par_Vector_I32F64_DOF3 & );
extern template void vec_scale(const double, par_Vector_I32F64_DOF4 & );


#endif